﻿using Athena.Http;
using BuddHa.Camera.Options;
using BuddHa.Camera.Protcol.DH.Models;
using BuddHa.Common.JsonOpex;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using System.Security.Cryptography;

namespace BuddHa.Camera.Protcol.DH
{
    public class DhHttpProtcol
    {
        public const string ClientName = "dh_http1.1";

        protected ILogger Logger { get; }

        protected IJsonOpexSerializer _jsonOpexSerializer { get; }

        protected IHttpRequester HttpRequester { get; }

        protected CameraOptions _cameraOptions { get; set; }

        public DhHttpProtcol(IJsonOpexSerializer jsonOpexSerializer, ILogger<DhHttpProtcol> logger, IOptions<CameraOptions> options, IHttpRequester httpRequester)
        {
            _jsonOpexSerializer = jsonOpexSerializer;
            Logger = logger;
            HttpRequester = httpRequester;
            _cameraOptions = options.Value;
        }

        public void InitRequestHeader(Dictionary<string, string> header, int contentLength, string token)
        {
            header.Add("X-Subject-Token", token);
            string contentType = "application/json;charset=UTF-8";
            header.Add("Content-Type", contentType);
            header.Add("Content-Length", contentLength.ToString());
        }

        /// <summary>
        /// 计算content-md5
        /// </summary>
        /// <param name="body"></param>
        /// <returns>base64后的content-md5</returns>
        private string ComputeContentMd5(string body)
        {
            StringBuilder sb = new StringBuilder();
            using (MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider())
            {
                //将输入字符串转换为字节数组并计算哈希。
                byte[] data = md5.ComputeHash(Encoding.UTF8.GetBytes(body));
                int length = data.Length;
                for (int i = 0; i < length; i++)
                    sb.Append(data[i].ToString("x2"));
            }
            return sb.ToString();
        }

        public string BuildSignature(string userName, string password, string realm, string randomKey)
        {
            string signature = ComputeContentMd5(password);
            signature = ComputeContentMd5(userName + signature);
            signature = ComputeContentMd5(signature);
            signature = ComputeContentMd5(userName + ":" + realm + ":" + signature);
            signature = ComputeContentMd5(signature + ":" + randomKey);
            return signature;
        }

        public async Task<FristAccountsAuthorizeResponse> FirstLogin(FristAccountsAuthorizeRequest fristAccountsAuthorizeRequest, string uri = "/videoService/accounts/authorize")
        {
            var sbUri = new StringBuilder();
            sbUri.Append(_cameraOptions.DhConfig.Gateway).Append(uri);
            var header = new Dictionary<string, string>();
            var strResponse = string.Empty;
            var body = JsonSerializer.Serialize(fristAccountsAuthorizeRequest);
            try
            {
                var sw = new Stopwatch();
                sw.Start();
                strResponse = await HttpRequester.PostAsync(sbUri.ToString(), body, ClientName, "", header, (reqHeader) =>
                {

                },
                (reqMes) =>
                {
                    reqMes.Version = HttpVersion.Version11;
                });
                sw.Stop();
                var tResponse = _jsonOpexSerializer.Deserialize<FristAccountsAuthorizeResponse>(strResponse);
                await InLog(uri, body, strResponse);
                return tResponse;
            }
            catch (Exception ex)
            {
                await InLog(uri, body, strResponse);
                throw new Exception($"大华第一次请求登录失败，resultmsg: {ex.Message}");
            }
        }

        public async Task<SecondAccountsAuthorizeResponse> SecondLogin(SecondAccountsAuthorizeRequest secondAccountsAuthorizeRequest, string uri = "/videoService/accounts/authorize")
        {

            var sbUri = new StringBuilder();
            sbUri.Append(_cameraOptions.DhConfig.Gateway).Append(uri);
            var header = new Dictionary<string, string>();
            var strResponse = string.Empty;
            var body = JsonSerializer.Serialize(secondAccountsAuthorizeRequest);
            try
            {
                var sw = new Stopwatch();
                sw.Start();
                strResponse = await HttpRequester.PostAsync(sbUri.ToString(), body, ClientName, "", header, (reqHeader) =>
                {

                },
                (reqMes) =>
                {
                    reqMes.Version = HttpVersion.Version11;
                });
                sw.Stop();
                var tResponse = _jsonOpexSerializer.Deserialize<SecondAccountsAuthorizeResponse>(strResponse);
                await InLog(uri, body, strResponse);
                return tResponse;
            }
            catch (Exception ex)
            {
                await InLog(uri, body, strResponse);
                throw new Exception($"大华第二次请求登录失败，resultmsg: {ex.Message}");
            }
        }

        public async Task<TokenKeepAliveResponse> KeepAliveRequest(TokenKeepAliveRequest tokenKeepAliveRequest, string token, string uri = "/videoService/accounts/token/keepalive")
        {
            var sbUri = new StringBuilder();
            sbUri.Append(_cameraOptions.DhConfig.Gateway).Append(uri);
            var header = new Dictionary<string, string>();
            header.Add("Host", _cameraOptions.DhConfig.IpAddress);
            header.Add("Connection", "close");
            header.Add("X-Subject-Token", token);
            var strResponse = string.Empty;
            var body = JsonSerializer.Serialize(tokenKeepAliveRequest);
            try
            {
                var sw = new Stopwatch();
                sw.Start();
                strResponse = await HttpRequester.PutAsync(sbUri.ToString(), body, ClientName, "", header,
                    (reqHeader) => {

                    },
                    (reqMes) => {
                        reqMes.Version = HttpVersion.Version11;
                    });
                sw.Stop();
                var tResponse = _jsonOpexSerializer.Deserialize<TokenKeepAliveResponse>(strResponse);
                await InLog(uri, body, strResponse);
                return tResponse;
            }
            catch (Exception ex)
            {
                await InLog(uri, body, strResponse);
                throw new Exception($"大华会话保活失败，resultmsg: {ex.Message}");
            }

        }

        public async Task<ManagerDeviceTreeResponse> ManagerDeviceTree(ManagerDeviceTreeRequest managerDeviceTreeRequest, string token, string uri = "/videoService/devicesManager/deviceTree?id={0}&nodeType={1}&typeCode={2}&page={3}&pageSize={4}")
        {
            var sbUri = new StringBuilder();
            sbUri.Append(_cameraOptions.DhConfig.Gateway).Append(uri);
            var header = new Dictionary<string, string>();
            header.Add("X-Subject-Token", token);
            var realUri = string.Format(sbUri.ToString(), managerDeviceTreeRequest.Id, managerDeviceTreeRequest.NodeType, managerDeviceTreeRequest.TypeCode, managerDeviceTreeRequest.Page, managerDeviceTreeRequest.PageSize);
            var strResponse = string.Empty;
            try
            {
                var sw = new Stopwatch();
                sw.Start();
                strResponse = await HttpRequester.GetAsync(realUri, ClientName, header,
                    (reqHeader) => {

                    },
                    (reqMes) => {
                        reqMes.Version = HttpVersion.Version11;
                    });
                sw.Stop();
                var tResponse = _jsonOpexSerializer.Deserialize<ManagerDeviceTreeResponse>(strResponse);
                await InLog(uri, string.Empty, strResponse);
                return tResponse;
            }
            catch (Exception ex)
            {
                await InLog(uri, string.Empty, strResponse);
                throw new Exception($"分级获取组织失败，resultmsg: {ex.Message}");
            }
        }

        public async Task<RealMonitorUriResponse> RealMonitorUri(RealMonitorUriRequest realMonitorUriRequest, string token, string uri = "/videoService/realmonitor/uri?channelId={0}&scheme={1}")
        {
            var sbUri = new StringBuilder();
            sbUri.Append(_cameraOptions.DhConfig.Gateway).Append(uri);
            var realUri = string.Format(sbUri.ToString(), realMonitorUriRequest.ChannelId, realMonitorUriRequest.Scheme);
            var header = new Dictionary<string, string>();
            header.Add("X-Subject-Token", token);
            var strResponse = string.Empty;
            try
            {
                var sw = new Stopwatch();
                sw.Start();
                strResponse = await HttpRequester.GetAsync(realUri, ClientName, header,
                    (reqHeader) => {

                    },
                    (reqMes) => {
                        reqMes.Version = HttpVersion.Version11;
                    });
                sw.Stop();
                var tResponse = _jsonOpexSerializer.Deserialize<RealMonitorUriResponse>(strResponse);
                await InLog(uri, string.Empty, strResponse);
                return tResponse;
            }
            catch (Exception ex)
            {
                await InLog(uri, string.Empty, strResponse);
                throw new Exception($"获取大华监控地址，resultmsg: {ex.Message}");
            }
        }

        public async Task<FileStreamResult> GetFileStreamResult(string uri)
        {
            var httpClient = new HttpClient();
            var stream = await httpClient.GetStreamAsync(uri);
            return new FileStreamResult(stream, "video/mp4");
        }

        public async Task InLog(string uri, string body, string strResponse)
        {
            //var quartzHttpLogAdapter = new QuartzHttpLogAdapter();
            //await quartzHttpLogAdapter.InsertAsync(new Adapter.Models.QuartzHttpLog()
            //{
            //    GuidRequest = Guid.NewGuid().ToString("N"),
            //    Uri = uri,
            //    RequestBody = body.Length > 255 ? body.Substring(0, 255) : body,
            //    ResponseBody = strResponse.Length > 255 ? strResponse.Substring(0, 255) : strResponse,
            //    ElapsedMilliseconds = 0,
            //    TimeCreate = DateTime.Now
            //});
        }
    }
}
